{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Part 2: 联邦学习简介\n",
    "\n",
    "在上一节中，我们了解了张量指针，它创建了隐私保护深度学习所需的基础架构。在本节中，我们将看到如何使用这些基本工具来实现我们的第一个隐私保护深度学习算法：联邦学习。\n",
    "\n",
    "作者:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "\n",
    "中文版译者：\n",
    "- Hou Wei - github：[@dljgs1](https://github.com/dljgs1)\n",
    "\n",
    "### 什么是联邦学习？\n",
    "\n",
    "它是训练深度学习模型的一种简单而强大的方法。考虑一下训练数据，一般它总是某种收集过程的结果：人们（通过设备）通过记录现实世界中的事件来生成数据。通常，此数据被聚合到单个中央位置，以便您可以训练机器学习模型。而联邦学习扭转了这一局面！\n",
    "\n",
    "你无需将训练数据带到模型（一个中央服务器），而是将模型带到训练数据（无论其位于何处）。\n",
    "\n",
    "这个想法允许创建数据的任何人拥有数据唯一的永久副本，从而保持对有权访问该数据的人的控制。很酷吧？"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2.1 一个玩具联合学习的例子\n",
    "\n",
    "让我们从一个集中式训练的玩具模型开始。就像得到模型一样简单。我们首先需要：\n",
    "\n",
    "- 玩具数据集\n",
    "- 一个模型\n",
    "- 用于训练模型以适合数据的一些基本训练逻辑。\n",
    "\n",
    "注意: 如果这些API对你很陌生，请转到教程[fast.ai](http://fast.ai)学习然后再继续。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "from torch import nn\n",
    "from torch import optim"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# 一个玩具数据集\n",
    "data = torch.tensor([[0,0],[0,1],[1,0],[1,1.]], requires_grad=True)\n",
    "target = torch.tensor([[0],[0],[1],[1.]], requires_grad=True)\n",
    "\n",
    "# 一个玩具模型\n",
    "model = nn.Linear(2,1)\n",
    "\n",
    "def train():\n",
    "    # 训练逻辑\n",
    "    opt = optim.SGD(params=model.parameters(),lr=0.1)\n",
    "    for iter in range(20):\n",
    "\n",
    "        # 1) 消除之前的梯度（如果存在）\n",
    "        opt.zero_grad()\n",
    "\n",
    "        # 2) 预测\n",
    "        pred = model(data)\n",
    "\n",
    "        # 3) 计算损失\n",
    "        loss = ((pred - target)**2).sum()\n",
    "\n",
    "        # 4) 指出那些导致损失的参数（损失回传）\n",
    "        loss.backward()\n",
    "\n",
    "        # 5) 更新参数\n",
    "        opt.step()\n",
    "\n",
    "        # 6) 打印进程\n",
    "        print(loss.data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "train()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在您拥有了它！我们已经以常规方式训练了基本模型。我们所有的数据都汇总到我们的本地计算机中，我们可以使用它来更新我们的模型。但是，联邦学习无法以这种方式工作。 因此，让我们修改此示例以实现联合学习方式！\n",
    "\n",
    "所以我们需要:\n",
    "\n",
    "- 创建一对工作机\n",
    "- 获取每个工作机关于训练数据的指针\n",
    "- 联邦学习的训练逻辑：\n",
    "    每一步新的训练步骤:\n",
    "    - 将模型发送给对应的工作机\n",
    "    - 在该机的数据上完成训练\n",
    "    - 拿回模型然后在下一个工作机重复"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import syft as sy\n",
    "hook = sy.TorchHook(torch)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建一对工作机\n",
    "\n",
    "bob = sy.VirtualWorker(hook, id=\"bob\")\n",
    "alice = sy.VirtualWorker(hook, id=\"alice\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 一个玩具数据集\n",
    "data = torch.tensor([[0,0],[0,1],[1,0],[1,1.]], requires_grad=True)\n",
    "target = torch.tensor([[0],[0],[1],[1.]], requires_grad=True)\n",
    "\n",
    "# 通过以下方式获取每个工作机的训练数据的指针\n",
    "# 向bob和alice发送一些训练数据\n",
    "data_bob = data[0:2]\n",
    "target_bob = target[0:2]\n",
    "\n",
    "data_alice = data[2:]\n",
    "target_alice = target[2:]\n",
    "\n",
    "# 初始化玩具模型\n",
    "model = nn.Linear(2,1)\n",
    "\n",
    "data_bob = data_bob.send(bob)\n",
    "data_alice = data_alice.send(alice)\n",
    "target_bob = target_bob.send(bob)\n",
    "target_alice = target_alice.send(alice)\n",
    "\n",
    "# 将指针组织到列表中\n",
    "datasets = [(data_bob,target_bob),(data_alice,target_alice)]\n",
    "\n",
    "opt = optim.SGD(params=model.parameters(),lr=0.1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train():\n",
    "    # 训练逻辑\n",
    "    opt = optim.SGD(params=model.parameters(),lr=0.1)\n",
    "    for iter in range(10):\n",
    "        \n",
    "        # NEW）遍历每个工作人员的数据集\n",
    "        for data,target in datasets:\n",
    "            \n",
    "            # NEW) 将模型发送给对应的工作机\n",
    "            model.send(data.location)\n",
    "\n",
    "            # 1) 消除之前的梯度（如果存在）\n",
    "            opt.zero_grad()\n",
    "\n",
    "            # 2) 预测\n",
    "            pred = model(data)\n",
    "\n",
    "            # 3) 计算损失\n",
    "            loss = ((pred - target)**2).sum()\n",
    "\n",
    "            # 4) 指出那些导致损失的参数（损失回传）\n",
    "            loss.backward()\n",
    "\n",
    "            # 5) 更新参数\n",
    "            opt.step()\n",
    "            \n",
    "            # NEW) 获取模型（带梯度）\n",
    "            model.get()\n",
    "\n",
    "            # 6) 打印进程\n",
    "            print(loss.data)\n",
    "    \n",
    "# 联合平均"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "train()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 做得好！\n",
    "\n",
    "瞧！我们现在正在使用联邦学习训练非常简单的深度学习模型！我们将模型发送给每个工作人员，生成新的梯度，然后将梯度带回我们的本地服务器，在此更新全局模型。在此过程中，我们永远不会看到或请求访问基础训练数据！我们保留Bob和Alice的隐私！\n",
    "\n",
    "## 这个例子的缺陷\n",
    "\n",
    "因此，尽管此示例是联合学习的不错介绍，但仍存在一些主要缺点。最值得注意的是，当我们调用`model.get()`并从Bob或Alice接收更新的模型时，我们实际上可以通过查看Bob和Alice的梯度来学习很多关于Bob和Alice的训练数据。在某些情况下，我们可以完美地恢复他们的训练数据！（译者注：此处属于隐私泄露攻击）\n",
    "\n",
    "那么，该怎么办？好吧，人们采用的第一个策略是**在将多个梯度上载到中央服务器之前对多个个体进行平均**。但是，此策略将需要对张量指针对象进行更复杂的使用。因此，在下一节中，我们将花费一些时间来学习更多高级指针功能，然后我们将升级此联邦学习示例。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 恭喜!!! 是时候加入社区了!\n",
    "\n",
    "祝贺您完成本笔记本教程！ 如果您喜欢此方法，并希望加入保护隐私、去中心化AI和AI供应链（数据）所有权的运动，则可以通过以下方式做到这一点！\n",
    "\n",
    "### 给 PySyft 加星\n",
    "\n",
    "帮助我们的社区的最简单方法是仅通过给GitHub存储库加注星标！ 这有助于提高人们对我们正在构建的出色工具的认识。\n",
    "\n",
    "- [Star PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### 加入我们的 Slack!\n",
    "\n",
    "保持最新进展的最佳方法是加入我们的社区！ 您可以通过填写以下表格来做到这一点[http://slack.openmined.org](http://slack.openmined.org)\n",
    "\n",
    "### 加入代码项目!\n",
    "\n",
    "对我们的社区做出贡献的最好方法是成为代码贡献者！ 您随时可以转到PySyft GitHub的Issue页面并过滤“projects”。这将向您显示所有概述，选择您可以加入的项目！如果您不想加入项目，但是想做一些编码，则还可以通过搜索标记为“good first issue”的GitHub问题来寻找更多的“一次性”微型项目。\n",
    "\n",
    "- [PySyft Projects](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### 捐赠\n",
    "\n",
    "如果您没有时间为我们的代码库做贡献，但仍想提供支持，那么您也可以成为Open Collective的支持者。所有捐款都将用于我们的网络托管和其他社区支出，例如黑客马拉松和聚会！\n",
    "\n",
    "[OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  },
  "latex_envs": {
   "LaTeX_envs_menu_present": true,
   "autoclose": false,
   "autocomplete": true,
   "bibliofile": "biblio.bib",
   "cite_by": "apalike",
   "current_citInitial": 1,
   "eqLabelWithNumbers": true,
   "eqNumInitial": 1,
   "hotkeys": {
    "equation": "Ctrl-E",
    "itemize": "Ctrl-I"
   },
   "labels_anchors": false,
   "latex_user_defs": false,
   "report_style_numbering": false,
   "user_envs_cfg": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
